package com.yugao.fintech.antelope.mps.service.impl;

import cn.hutool.core.lang.func.LambdaUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yugao.fintech.antelope.infra.module.dict.DictItemApi;
import com.yugao.fintech.antelope.infra.module.dict.model.entity.DictItem;
import com.yugao.fintech.antelope.base.model.module.dict.DictTypeCons;
import com.yugao.fintech.antelope.mps.assembly.MsgTemplateAssembly;
import com.yugao.fintech.antelope.mps.core.parser.DefaultTemplateParser;
import com.yugao.fintech.antelope.mps.manager.MsgFieldManager;
import com.yugao.fintech.antelope.mps.manager.MsgHistoryManager;
import com.yugao.fintech.antelope.mps.manager.MsgPushTaskManager;
import com.yugao.fintech.antelope.mps.manager.MsgTemplateManager;
import com.yugao.fintech.antelope.mps.mapper.MsgTemplateMapper;
import com.yugao.fintech.antelope.mps.model.entity.MsgField;
import com.yugao.fintech.antelope.mps.model.entity.MsgPushTask;
import com.yugao.fintech.antelope.mps.model.entity.MsgTemplate;
import com.yugao.fintech.antelope.mps.model.rqrs.template.MsgTemplatePageReq;
import com.yugao.fintech.antelope.mps.model.rqrs.template.MsgTemplateResp;
import com.yugao.fintech.antelope.mps.model.rqrs.template.MsgTemplateSaveReq;
import com.yugao.fintech.antelope.mps.model.rqrs.template.MsgTemplateUpdateReq;
import com.yugao.fintech.antelope.mps.service.MsgTemplateService;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.desensitization.annotations.FieldBindHandle;
import com.yugao.fintech.framework.mybatisplus.utils.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MsgTemplateServiceImpl extends ServiceImpl<MsgTemplateMapper, MsgTemplate> implements IService<MsgTemplate>, MsgTemplateService {
    @Autowired
    private MsgTemplateManager msgTemplateManager;
    @Autowired
    private MsgPushTaskManager pushTaskManager;
    @Autowired
    private MsgTemplateAssembly msgTemplateAssembly;
    @Autowired
    private MsgHistoryManager historyManager;
    @Autowired
    private DictItemApi dictItemApi;
    @Autowired
    private MsgFieldManager msgFieldManager;
    
    @Override
    @FieldBindHandle
    public IPage<MsgTemplateResp> pageTemplate(MsgTemplatePageReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<MsgTemplate> lqw = MsgTemplate.lqw()
            .eq(Objects.nonNull(req.getTaskId()), MsgTemplate::getTaskId, req.getTaskId())
            .eq(StringUtils.isNotEmpty(req.getChannelType()), MsgTemplate::getChannelType, req.getChannelType())
            .eq(StringUtils.isNotEmpty(req.getStatus()), MsgTemplate::getStatus, req.getStatus())
            .ge(Objects.nonNull(beginTime), MsgTemplate::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), MsgTemplate::getCreatedAt, endTime)
            .orderByDesc(MsgTemplate::getCreatedAt);
        IPage<MsgTemplate> pageData = this.page(PageUtil.to(req), lqw);
        Set<Long> taskIds = pageData.getRecords().stream().map(MsgTemplate::getTaskId).collect(Collectors.toSet());
        taskIds.add(-1L);

        Map<Long, MsgPushTask> taskMap = pushTaskManager.listByIds(taskIds).stream()
                .collect(Collectors.toMap(MsgPushTask::getId, Function.identity()));

        return PageUtil.to(pageData, e -> {
            MsgTemplateResp resp = msgTemplateAssembly.toMsgTemplateResp(e);
            Optional.ofNullable(taskMap.get(e.getTaskId())).ifPresent(e1 -> {
                resp.setTaskName(e1.getName());
                resp.setTitle(StringUtils.defaultIfEmpty(resp.getTitle(), e1.getName()));
                resp.setTaskCode(e1.getTaskCode());
            });
            return resp;
        });
    }
    
    @Override
    public List<MsgTemplateResp> listTemplate() {
    	return this.list().stream()
    	        .map(msgTemplateAssembly::toMsgTemplateResp).collect(Collectors.toList());
    }

    @Override
    @FieldBindHandle
    public MsgTemplateResp getTemplate(Long id) {
        MsgTemplate entity = this.getById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        MsgPushTask task = pushTaskManager.getById(entity.getTaskId());
        List<MsgField> fieldList = new ArrayList<>();
        if (Objects.nonNull(task)) {
            // 查询字段
            fieldList = msgFieldManager.list(MsgField.lqw().eq(MsgField::getTaskId, task.getId()));
        }

        updateTemplateField(fieldList, entity);

        MsgTemplateResp resp = msgTemplateAssembly.toMsgTemplateResp(entity);
        resp.setTaskName(Optional.ofNullable(task).map(MsgPushTask::getName).orElse("未知"));
        return resp;
    }

    /**
     * 模板中的字段都是从任务中定义的字段选的, 如果任务配置的字段发生变更, 比如删除或者更新字段名, 那么
     * 要从所有模板中删除(本次采用惰性删除)
     */
    public void updateTemplateField(List<MsgField> fields, MsgTemplate template) {
        Map<String, MsgField> fieldMap = fields.stream().collect(Collectors.toMap(MsgField::getFieldName, Function.identity()));
        String templateConfigStr = template.getTemplateConfig();
        DefaultTemplateParser templateConfig = JSONObject.parseObject(templateConfigStr, DefaultTemplateParser.class);

        List<String> deleteKeys = templateConfig.getVarData().keySet().stream().filter(e -> !fieldMap.containsKey(e))
                .collect(Collectors.toList());
        deleteKeys.forEach(e -> templateConfig.getVarData().remove(e));

        JSONObject jsonObject = JSON.parseObject(templateConfigStr);
        jsonObject.put(LambdaUtil.getFieldName(DefaultTemplateParser::getVarData), templateConfig.getVarData());
        template.setTemplateConfig(jsonObject.toJSONString());
    }

    @Override
    public boolean saveTemplate(MsgTemplateSaveReq req) {
        MsgTemplate entity = msgTemplateAssembly.toMsgTemplate(req);
        validTemplate(null, req.getTaskId(), req.getChannelType());

    	return this.save(entity);
    }

    public void validTemplate(Long templateId, Long taskId, String channelType) {
        // 校验任务是否存在
        MsgPushTask pushTask = this.pushTaskManager.getById(taskId);
        if (Objects.isNull(pushTask)) {
            throw new BizException("任务不存在");
        }

        // 校验某个任务下是否已存在某个消息模板
        LambdaQueryWrapper<MsgTemplate> lqw = MsgTemplate.lqw().eq(MsgTemplate::getTaskId, taskId)
                .ne(Objects.nonNull(templateId), MsgTemplate::getId, templateId)
                .eq(MsgTemplate::getChannelType, channelType);
        if (msgTemplateManager.count(lqw) > 0) {
            DictItem dictItem = dictItemApi.getDictItem(DictTypeCons.MESSAGE_CHANNEL_TYPE, channelType);
            throw new BizException("任务 【{}】 中已存在 【{}】 消息渠道", pushTask.getName(), dictItem.getItemLabel());
        }
    }

    @Override
    public boolean updateTemplate(MsgTemplateUpdateReq req) {
        // 如果为空, 就不设置模板配置
        if ("{}".equals(StringUtils.defaultString(req.getTemplateConfig(), "{}").replace(" ", ""))) {
            req.setTemplateConfig(null);
        }
        MsgTemplate oldTemplate = this.getById(req.getId());
        if (Objects.isNull(oldTemplate)) {
            throw new BizException("模板不存在");
        }
        MsgTemplate entity = msgTemplateAssembly.toMsgTemplate(req);
        validTemplate(entity.getId(), req.getTaskId(), oldTemplate.getChannelType());
    	return this.updateById(entity);
    }

    @Override
    public void updateTemplateStatus(Long id, Boolean status) {
        this.update(MsgTemplate.luw().eq(MsgTemplate::getId, id).set(MsgTemplate::getStatus, status));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTemplate(Long id) {
        historyManager.deleteHistoryByTemplateIds(Collections.singletonList(id));
    	return this.removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteTemplate(List<Long> ids) {
        historyManager.deleteHistoryByTemplateIds(ids);
    	return this.removeByIds(ids);
    }
}
